namespace RestaurantFinder.Core.Models;

/// <summary>
/// 搜索缓存键，用于标识唯一的搜索请求
/// </summary>
public record SearchCacheKey
{
    /// <summary>
    /// 纬度（保留3位小数，约110米精度）
    /// </summary>
    public double Latitude { get; init; }

    /// <summary>
    /// 经度（保留3位小数，约110米精度）
    /// </summary>
    public double Longitude { get; init; }

    /// <summary>
    /// 搜索半径（米）
    /// </summary>
    public int RadiusInMeters { get; init; }

    /// <summary>
    /// 搜索类别（可选）
    /// </summary>
    public string? Category { get; init; }

    /// <summary>
    /// 构造函数
    /// </summary>
    public SearchCacheKey(Location location, int radiusInMeters, string? category = null)
    {
        // 将坐标舍入到指定精度以提高缓存命中率
        Latitude = Math.Round(location.Latitude, 3);
        Longitude = Math.Round(location.Longitude, 3);
        RadiusInMeters = radiusInMeters;
        Category = category;
    }

    /// <summary>
    /// 生成缓存键字符串
    /// </summary>
    public override string ToString()
    {
        var categoryPart = string.IsNullOrEmpty(Category) ? "" : $"_{Category}";
        return $"search_{Latitude:F3}_{Longitude:F3}_{RadiusInMeters}{categoryPart}";
    }

    /// <summary>
    /// 检查两个位置是否在指定容差范围内
    /// </summary>
    public bool IsWithinTolerance(Location location, int toleranceMeters)
    {
        var currentLocation = new Location(Latitude, Longitude);
        var distance = CalculateDistance(currentLocation, location);
        return distance <= toleranceMeters;
    }

    /// <summary>
    /// 计算两点间距离（米）
    /// </summary>
    private static double CalculateDistance(Location point1, Location point2)
    {
        const double earthRadius = 6371000; // 地球半径（米）
        
        var lat1Rad = point1.Latitude * Math.PI / 180;
        var lat2Rad = point2.Latitude * Math.PI / 180;
        var deltaLatRad = (point2.Latitude - point1.Latitude) * Math.PI / 180;
        var deltaLonRad = (point2.Longitude - point1.Longitude) * Math.PI / 180;

        var a = Math.Sin(deltaLatRad / 2) * Math.Sin(deltaLatRad / 2) +
                Math.Cos(lat1Rad) * Math.Cos(lat2Rad) *
                Math.Sin(deltaLonRad / 2) * Math.Sin(deltaLonRad / 2);
        
        var c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
        
        return earthRadius * c;
    }
} 